
package hasthi.common;

import hasthi.common.constants.HasthiConstants;
import hasthi.container.notifier.WSMessengerNotifier;
import hasthi.manager.ManagementLogMessageDocument;
import hasthi.manager.ManagementLogMessageDocument.ManagementLogMessage;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.concurrent.LinkedBlockingQueue;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class DistributedLogger {
    private static Log log = LogFactory.getLog(HasthiConstants.LOGGER_NAME);
    private static enum LogLevel{info, debug, error, warning,fatal};
    private final ManagementNotifier notifier;
    private BaseContext baseConfigContext;
    private Thread sender;
    private boolean sendEvents = true;
    
    /**
     * This is a queue, which do not have a limit and blocks if it is empty
     */
    private LinkedBlockingQueue<String[]> blockingQueue = new LinkedBlockingQueue<String[]>();
    
    
    public DistributedLogger(BaseContext baseConfigContext){
        this.baseConfigContext = baseConfigContext;
        notifier = new WSMessengerNotifier(baseConfigContext.getMessageBrokerUrl(),baseConfigContext.getManagementTopic(),"DistributedLogger");
        sender = new Thread(new DlogSender());
        sender.setDaemon(true);
        sender.start();
    }
    
    public void info(String... messsage){
        StringBuffer buf = new StringBuffer();
        if(messsage != null){
            for(String part:messsage){
                buf.append(part);
            }
        }
        String finalMsg = buf.toString();
        sendMessage(LogLevel.info, finalMsg, null);
        log.info(finalMsg);
    }
    
    
    public void info(String messsage){
        sendMessage(LogLevel.info, messsage, null);
        log.info(messsage);
    }
    public void debug(String messsage){
        sendMessage(LogLevel.debug, messsage, null);
        log.debug(messsage);
    }
    public void errror(String messsage,Throwable e){
        sendMessage(LogLevel.error, messsage, e);
        
        if(e != null){
            log.error(messsage,e);
            ErrorManager.error(e);
            e.printStackTrace();
        }else{
            log.error(messsage);
        }
    }
    public void warrning(String messsage){
        sendMessage(LogLevel.warning, messsage, null);
        log.warn(messsage);
    }
    
    public void warrning(String messsage,Throwable e){
        sendMessage(LogLevel.warning, messsage, e);
        log.warn(messsage,e);
    }
    public void fatal(String messsage,Throwable e){
        sendMessage(LogLevel.fatal, messsage, e);
        log.fatal(messsage,e);
        e.printStackTrace();
    }
    
    protected void sendMessage(LogLevel level,String message,Throwable e){
        if(!sendEvents){
            return;
        }
        message = new StringBuffer().append("(")
            .append(System.currentTimeMillis())
            .append(")").append(message).toString();
        try {
            String[] log;
            if(e !=  null){
                log = new String[3];
                StringWriter strWr = new StringWriter();
                PrintWriter pw = new PrintWriter(strWr);
                e.printStackTrace(pw);
                pw.close();
                log[2] = strWr.getBuffer().toString();
            }else{
                log = new String[2];
            }
            log[0] = level.toString();
            log[1] = message;
            blockingQueue.put(log);
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }
    }
    
    
    protected ManagementLogMessageDocument createBaseElement(){
        ManagementLogMessageDocument messageDocument = ManagementLogMessageDocument.Factory.newInstance();
        ManagementLogMessage logMessage = messageDocument.addNewManagementLogMessage();
        //logMessage.setType(level.toString());
        logMessage.setSender(baseConfigContext.getName());
        return messageDocument;
    }
    
    public class DlogSender implements Runnable{
        ManagementLogMessageDocument logMessageTemplate = createBaseElement();
        public void run() {
            while(true){
                try {
                    String[] log = blockingQueue.take();
                    ManagementLogMessage logMessage = logMessageTemplate.getManagementLogMessage();
                    logMessage.setSender(baseConfigContext.getName());
                    logMessage.setType(log[0]);
                    logMessage.setMesssage(log[1]);
                    if(log.length == 3){
                        logMessage.setDetail(log[2]);
                    }
                    try {
                        notifier.sendEvent(logMessageTemplate);
                    } catch (HasthiException e) {
                        DistributedLogger.log.debug("Failed seding events via distributed logger",e);
                        e.printStackTrace();
                    }
                } catch (Throwable e) {
                    log.debug("Failed seding events via distributed logger",e);
                }
            }
        }
    }
}

